import Dep from "./dep";

let uid = 0;
// 不同的组件有不同的watcher
// 一个组件有多个属性，每个属性都有一个dep
// dep的目的是收集watcher
// 一个dep有多个watcher
// 一个watcher可以对应多个dep
class Watcher {
    constructor(vm, fn) {
        this.id = uid++;
        this.getter = fn;
        this.deps = []; // 存放dep 后续实现计算属性，和一些清理工作
        this.depsId = new Set();
        this.get();
    }

    get() {
        Dep.target = this;
        this.getter();
        Dep.target = null;
    }

    update() {
        queueWatcher(this);
    }

    run() {
        this.get();
    }

    //收集属性dep
    addDep(dep) {
        let id = dep.id;
        // 防止重复收集属性dep
        if (!this.depsId.has(id)) {
            this.depsId.add(id);
            this.deps.push(dep);
            dep.addSub(this);
        }
    }
}

let hasWatch = {};
let queue = [];
let waiting = false;
function queueWatcher(watcher) {
    const id = watcher.id;
    if (!hasWatch[id]) {
        hasWatch[id] = true;
        queue.push(watcher);
        //waiting 保证在同一事件循环中只开启一次定时器，将多个watcher合并成一个
        if (!waiting) {
            waiting = true;
            nextTick(flushSchedulerQueue);
        }
    }
}

function flushSchedulerQueue() {
    let flushQueue = queue.slice(0);
    queue = [];
    hasWatch = {};
    waiting = false;
    flushQueue.forEach((watcher) => {
        watcher.run();
    }); // 在刷新过程中可能会再次添加watcher，所以需要将queue中的watcher执行完后再清空
}

let timeFunc;
if (Promise) {
    timeFunc = () => {
        Promise.resolve().then(flushCallbacks);
    };
} else if (MutationObserver) {
    let textNode = document.createTextNode(1);
    let observe = new MutationObserver(flushCallbacks);
    observe.observe(textNode, {
        characterData: true,
    });
    timeFunc = () => {
        textNode.data = 2;
    };
} else if (setImmediate) {
    timeFunc = () => {
        setImmediate(flushCallbacks);
    };
} else {
    timeFunc = () => {
        setTimeout(flushCallbacks, 0);
    };
}



let callbacks = [];
let pending = false;
function flushCallbacks() {
    pending = false;
    const copies = callbacks.slice(0);
    callbacks = [];
    copies.forEach(cb => cb());
}

export function nextTick(cb) {
    callbacks.push(cb);
    if (!pending) {
        pending = true;
        timeFunc();
        // vue3 Promise.resolve().then(flushCallbacks);
    }
}
export default Watcher;
